<?php
// +----------------------------------------------
// | Created by PhpStorm
// +----------------------------------------------
// | Author: YnnSon
// +----------------------------------------------
namespace app\api\service;

use app\api\model\UserWeixin as UserWeixinModel;
use app\lib\enum\ScopeEnum;
use app\lib\exception\TokenException;
use think\Cache;
use think\Config;
use think\Exception;
use Yurun\Util\HttpRequest;

/**
 * 用户Token
 * Class UserToken
 * @package app\api\service
 */
class UserToken extends Token
{
    private $loginUrl;

    public function __construct($code)
    {
        $appId = Config::get('wechat.appid');
        $appSecret = Config::get('wechat.appsecret');
        $this->loginUrl = sprintf(Config::get('wechat.login_url'), $appId, $appSecret, $code);
    }

    /**
     * @success {"session_key":"+osH7n97XgnhFGZCZkLjZw==","openid":"o_NJp5KPTW5XYbyWY8OW_qDZke3U"}
     * @Desc 向微信服务器发送请求来获取openid
     * @return array ['token' => token]
     */
    public function get()
    {
        $http = new HttpRequest;
        $response = $http->get($this->loginUrl)->body();
        $wxResult = json_decode($response, true);
        if (empty($wxResult)) {
            throw new Exception('获取openid和session_key时异常, 微信内部错误');
        }
        if (array_key_exists('errcode', $wxResult)) {
            throw new TokenException([
                'message'   => $wxResult['errmsg'],
                'errorCode' => $wxResult['errcode'],
            ]);
        }
        $token = $this->grantToken($wxResult);
        return ['token' => $token];
    }

    /**
     * @Desc 生成token
     *
     * @param array $wxResult 微信返回值: openid+session_key
     *
     * @return string token
     */
    private function grantToken(array $wxResult)
    {
        // 1.获取openid
        // $openid = $wxResult['openid'];
        // 2.判断数据库中是否存在, 存在返回uid, 不存在存入数据库
        $user = UserWeixinModel::getUserByOpenid($wxResult['openid']);
        if (null != $user) {
            $uid = $user->id;
        } else {
            $uid = UserWeixinModel::insertUserByOpenid($wxResult);
        }
        // 3.用随机字符串+时间戳+salt的md5值生成token的key
        $key = self::generateToken();
        // 4.用数组openid+uid+scope转换的json生成token的value
        $value = $this->generateTokenValue($wxResult, $uid);
        // 5.将token加入缓存, 设置有效期
        // 6.将token传回客户端
        return $this->setTokenCache($key, $value);
    }

    /**
     * @Desc 生成令牌的value, 由数组openid+uid+scope转换的json组成
     *
     * @param array $wxResult openid+session_key
     * @param int   $uid
     *
     * @return string value 缓存值: openid+uid+scope
     */
    private function generateTokenValue(array $wxResult, $uid)
    {
        $value = [
            'uid'   => $uid,
            'scope' => ScopeEnum::USER,
        ];
        $tokenValue = array_merge($wxResult, $value);
        return json_encode($tokenValue);
    }

    /**
     * @Desc 将token加入缓存
     *
     * @param string $key   缓存键: token
     * @param string $value 缓存值: openid+uid+scope
     *
     * @return string token
     */
    private function setTokenCache($key, $value)
    {
        // 5.设置有效期
        $expire = Config::get('setting.token_expire_in');
        // 6.将token加入缓存
        $result = Cache::set($key, $value, $expire);
        if (!$result) {
            throw new TokenException();
        }
        return $key;
    }
}